ocLazyLoad.js ➔ broadcast   B
last analyzed

Complexity

Conditions 7

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 8
rs 8
c 0
b 0
f 0
cc 7
1
/**
2
 * oclazyload - Load modules on demand (lazy load) with angularJS
3
 * @version v1.0.10
4
 * @link https://github.com/ocombe/ocLazyLoad
5
 * @license MIT
6
 * @author Olivier Combe <[email protected]>
7
 */
8
(function (angular, window) {
9
    'use strict';
10
11
    var regModules = ['ng', 'oc.lazyLoad'],
12
        regInvokes = {},
13
        regConfigs = [],
14
        modulesToLoad = [],
15
        // modules to load from angular.module or other sources
16
    realModules = [],
17
        // real modules called from angular.module
18
    recordDeclarations = [],
19
        broadcast = angular.noop,
20
        runBlocks = {},
21
        justLoaded = [];
22
23
    var ocLazyLoad = angular.module('oc.lazyLoad', ['ng']);
24
25
    ocLazyLoad.provider('$ocLazyLoad', ["$controllerProvider", "$provide", "$compileProvider", "$filterProvider", "$injector", "$animateProvider", function ($controllerProvider, $provide, $compileProvider, $filterProvider, $injector, $animateProvider) {
26
        var modules = {},
27
            providers = {
28
            $controllerProvider: $controllerProvider,
29
            $compileProvider: $compileProvider,
30
            $filterProvider: $filterProvider,
31
            $provide: $provide, // other things (constant, decorator, provider, factory, service)
32
            $injector: $injector,
33
            $animateProvider: $animateProvider
34
        },
35
            debug = false,
36
            events = false,
37
            moduleCache = [],
38
            modulePromises = {};
39
40
        moduleCache.push = function (value) {
41
            if (this.indexOf(value) === -1) {
42
                Array.prototype.push.apply(this, arguments);
43
            }
44
        };
45
46
        this.config = function (config) {
47
            // If we want to define modules configs
48
            if (angular.isDefined(config.modules)) {
49
                if (angular.isArray(config.modules)) {
50
                    angular.forEach(config.modules, function (moduleConfig) {
51
                        modules[moduleConfig.name] = moduleConfig;
52
                    });
53
                } else {
54
                    modules[config.modules.name] = config.modules;
55
                }
56
            }
57
58
            if (angular.isDefined(config.debug)) {
59
                debug = config.debug;
60
            }
61
62
            if (angular.isDefined(config.events)) {
63
                events = config.events;
64
            }
65
        };
66
67
        /**
68
         * Get the list of existing registered modules
69
         * @param element
70
         */
71
        this._init = function _init(element) {
72
            // this is probably useless now because we override angular.bootstrap
73
            if (modulesToLoad.length === 0) {
74
                var elements = [element],
75
                    names = ['ng:app', 'ng-app', 'x-ng-app', 'data-ng-app'],
76
                    NG_APP_CLASS_REGEXP = /\sng[:\-]app(:\s*([\w\d_]+);?)?\s/,
77
                    append = function append(elm) {
78
                    return elm && elements.push(elm);
79
                };
80
81
                angular.forEach(names, function (name) {
82
                    names[name] = true;
83
                    append(document.getElementById(name));
84
                    name = name.replace(':', '\\:');
85
                    if (typeof element[0] !== 'undefined' && element[0].querySelectorAll) {
86
                        angular.forEach(element[0].querySelectorAll('.' + name), append);
87
                        angular.forEach(element[0].querySelectorAll('.' + name + '\\:'), append);
88
                        angular.forEach(element[0].querySelectorAll('[' + name + ']'), append);
89
                    }
90
                });
91
92
                angular.forEach(elements, function (elm) {
93
                    if (modulesToLoad.length === 0) {
94
                        var className = ' ' + element.className + ' ';
95
                        var match = NG_APP_CLASS_REGEXP.exec(className);
96
                        if (match) {
97
                            modulesToLoad.push((match[2] || '').replace(/\s+/g, ','));
98
                        } else {
99
                            angular.forEach(elm.attributes, function (attr) {
100
                                if (modulesToLoad.length === 0 && names[attr.name]) {
101
                                    modulesToLoad.push(attr.value);
102
                                }
103
                            });
104
                        }
105
                    }
106
                });
107
            }
108
109
            if (modulesToLoad.length === 0 && !((window.jasmine || window.mocha) && angular.isDefined(angular.mock))) {
110
                console.error('No module found during bootstrap, unable to init ocLazyLoad. You should always use the ng-app directive or angular.boostrap when you use ocLazyLoad.');
111
            }
112
113
            var addReg = function addReg(moduleName) {
114
                if (regModules.indexOf(moduleName) === -1) {
115
                    // register existing modules
116
                    regModules.push(moduleName);
117
                    var mainModule = angular.module(moduleName);
118
119
                    // register existing components (directives, services, ...)
120
                    _invokeQueue(null, mainModule._invokeQueue, moduleName);
121
                    _invokeQueue(null, mainModule._configBlocks, moduleName); // angular 1.3+
122
123
                    angular.forEach(mainModule.requires, addReg);
124
                }
125
            };
126
127
            angular.forEach(modulesToLoad, function (moduleName) {
128
                addReg(moduleName);
129
            });
130
131
            modulesToLoad = []; // reset for next bootstrap
132
            recordDeclarations.pop(); // wait for the next lazy load
133
        };
134
135
        /**
136
         * Like JSON.stringify but that doesn't throw on circular references
137
         * @param obj
138
         */
139
        var stringify = function stringify(obj) {
140
            try {
141
                return JSON.stringify(obj);
142
            } catch (e) {
143
                var cache = [];
144
                return JSON.stringify(obj, function (key, value) {
145
                    if (angular.isObject(value) && value !== null) {
146
                        if (cache.indexOf(value) !== -1) {
147
                            // Circular reference found, discard key
148
                            return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
149
                        }
150
                        // Store value in our collection
151
                        cache.push(value);
152
                    }
153
                    return value;
154
                });
155
            }
156
        };
157
158
        var hashCode = function hashCode(str) {
159
            var hash = 0,
160
                i,
161
                chr,
162
                len;
163
            if (str.length == 0) {
164
                return hash;
165
            }
166
            for (i = 0, len = str.length; i < len; i++) {
167
                chr = str.charCodeAt(i);
168
                hash = (hash << 5) - hash + chr;
169
                hash |= 0; // Convert to 32bit integer
170
            }
171
            return hash;
172
        };
173
174
        function _register(providers, registerModules, params) {
175
            if (registerModules) {
176
                var k,
177
                    moduleName,
178
                    moduleFn,
179
                    tempRunBlocks = [];
180
                for (k = registerModules.length - 1; k >= 0; k--) {
181
                    moduleName = registerModules[k];
182
                    if (!angular.isString(moduleName)) {
183
                        moduleName = getModuleName(moduleName);
184
                    }
185
                    if (!moduleName || justLoaded.indexOf(moduleName) !== -1 || modules[moduleName] && realModules.indexOf(moduleName) === -1) {
186
                        continue;
187
                    }
188
                    // new if not registered
189
                    var newModule = regModules.indexOf(moduleName) === -1;
190
                    moduleFn = ngModuleFct(moduleName);
191
                    if (newModule) {
192
                        regModules.push(moduleName);
193
                        _register(providers, moduleFn.requires, params);
194
                    }
195
                    if (moduleFn._runBlocks.length > 0) {
196
                        // new run blocks detected! Replace the old ones (if existing)
197
                        runBlocks[moduleName] = [];
198
                        while (moduleFn._runBlocks.length > 0) {
199
                            runBlocks[moduleName].push(moduleFn._runBlocks.shift());
200
                        }
201
                    }
202
                    if (angular.isDefined(runBlocks[moduleName]) && (newModule || params.rerun)) {
203
                        tempRunBlocks = tempRunBlocks.concat(runBlocks[moduleName]);
204
                    }
205
                    _invokeQueue(providers, moduleFn._invokeQueue, moduleName, params.reconfig);
206
                    _invokeQueue(providers, moduleFn._configBlocks, moduleName, params.reconfig); // angular 1.3+
207
                    broadcast(newModule ? 'ocLazyLoad.moduleLoaded' : 'ocLazyLoad.moduleReloaded', moduleName);
208
                    registerModules.pop();
209
                    justLoaded.push(moduleName);
210
                }
211
                // execute the run blocks at the end
212
                var instanceInjector = providers.getInstanceInjector();
213
                angular.forEach(tempRunBlocks, function (fn) {
214
                    instanceInjector.invoke(fn);
215
                });
216
            }
217
        }
218
219
        function _registerInvokeList(args, moduleName) {
220
            var invokeList = args[2][0],
221
                type = args[1],
222
                newInvoke = false;
223
            if (angular.isUndefined(regInvokes[moduleName])) {
224
                regInvokes[moduleName] = {};
225
            }
226
            if (angular.isUndefined(regInvokes[moduleName][type])) {
227
                regInvokes[moduleName][type] = {};
228
            }
229
            var onInvoke = function onInvoke(invokeName, invoke) {
230
                if (!regInvokes[moduleName][type].hasOwnProperty(invokeName)) {
231
                    regInvokes[moduleName][type][invokeName] = [];
232
                }
233
                if (checkHashes(invoke, regInvokes[moduleName][type][invokeName])) {
234
                    newInvoke = true;
235
                    regInvokes[moduleName][type][invokeName].push(invoke);
236
                    broadcast('ocLazyLoad.componentLoaded', [moduleName, type, invokeName]);
237
                }
238
            };
239
240
            function checkHashes(potentialNew, invokes) {
241
                var isNew = true,
242
                    newHash;
243
                if (invokes.length) {
244
                    newHash = signature(potentialNew);
245
                    angular.forEach(invokes, function (invoke) {
246
                        isNew = isNew && signature(invoke) !== newHash;
247
                    });
248
                }
249
                return isNew;
250
            }
251
252
            function signature(data) {
253
                if (angular.isArray(data)) {
254
                    // arrays are objects, we need to test for it first
255
                    return hashCode(data.toString());
256
                } else if (angular.isObject(data)) {
257
                    // constants & values for example
258
                    return hashCode(stringify(data));
259
                } else {
260
                    if (angular.isDefined(data) && data !== null) {
261
                        return hashCode(data.toString());
262
                    } else {
263
                        // null & undefined constants
264
                        return data;
265
                    }
266
                }
267
            }
268
269
            if (angular.isString(invokeList)) {
270
                onInvoke(invokeList, args[2][1]);
271
            } else if (angular.isObject(invokeList)) {
272
                angular.forEach(invokeList, function (invoke, key) {
273
                    if (angular.isString(invoke)) {
274
                        // decorators for example
275
                        onInvoke(invoke, invokeList[1]);
276
                    } else {
277
                        // components registered as object lists {"componentName": function() {}}
278
                        onInvoke(key, invoke);
279
                    }
280
                });
281
            } else {
282
                return false;
283
            }
284
            return newInvoke;
285
        }
286
287
        function _invokeQueue(providers, queue, moduleName, reconfig) {
288
            if (!queue) {
289
                return;
290
            }
291
292
            var i, len, args, provider;
293
            for (i = 0, len = queue.length; i < len; i++) {
294
                args = queue[i];
295
                if (angular.isArray(args)) {
296
                    if (providers !== null) {
297
                        if (providers.hasOwnProperty(args[0])) {
298
                            provider = providers[args[0]];
299
                        } else {
300
                            throw new Error('unsupported provider ' + args[0]);
301
                        }
302
                    }
303
                    var isNew = _registerInvokeList(args, moduleName);
304
                    if (args[1] !== 'invoke') {
305
                        if (isNew && angular.isDefined(provider)) {
0 ignored issues
show
Bug introduced by
The variable provider seems to not be initialized for all possible execution paths. Are you sure isDefined handles undefined variables?
Loading history...
306
                            provider[args[1]].apply(provider, args[2]);
307
                        }
308
                    } else {
309
                        // config block
310
                        var callInvoke = function callInvoke(fct) {
311
                            var invoked = regConfigs.indexOf(moduleName + '-' + fct);
312
                            if (invoked === -1 || reconfig) {
313
                                if (invoked === -1) {
314
                                    regConfigs.push(moduleName + '-' + fct);
315
                                }
316
                                if (angular.isDefined(provider)) {
0 ignored issues
show
Bug introduced by
The variable provider seems to not be initialized for all possible execution paths. Are you sure isDefined handles undefined variables?
Loading history...
317
                                    provider[args[1]].apply(provider, args[2]);
0 ignored issues
show
Bug introduced by
The variable args is changed as part of the for loop for example by queue.i on line 294. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
318
                                }
319
                            }
320
                        };
321
                        if (angular.isFunction(args[2][0])) {
322
                            callInvoke(args[2][0]);
323
                        } else if (angular.isArray(args[2][0])) {
324
                            for (var j = 0, jlen = args[2][0].length; j < jlen; j++) {
325
                                if (angular.isFunction(args[2][0][j])) {
326
                                    callInvoke(args[2][0][j]);
327
                                }
328
                            }
329
                        }
330
                    }
331
                }
332
            }
333
        }
334
335
        function getModuleName(module) {
336
            var moduleName = null;
337
            if (angular.isString(module)) {
338
                moduleName = module;
339
            } else if (angular.isObject(module) && module.hasOwnProperty('name') && angular.isString(module.name)) {
340
                moduleName = module.name;
341
            }
342
            return moduleName;
343
        }
344
345
        function moduleExists(moduleName) {
346
            if (!angular.isString(moduleName)) {
347
                return false;
348
            }
349
            try {
350
                return ngModuleFct(moduleName);
351
            } catch (e) {
352
                if (/No module/.test(e) || e.message.indexOf('$injector:nomod') > -1) {
353
                    return false;
354
                }
355
            }
356
        }
357
358
        this.$get = ["$log", "$rootElement", "$rootScope", "$cacheFactory", "$q", function ($log, $rootElement, $rootScope, $cacheFactory, $q) {
359
            var instanceInjector,
360
                filesCache = $cacheFactory('ocLazyLoad');
361
362
            if (!debug) {
363
                $log = {};
364
                $log['error'] = angular.noop;
365
                $log['warn'] = angular.noop;
366
                $log['info'] = angular.noop;
367
            }
368
369
            // Make this lazy because when $get() is called the instance injector hasn't been assigned to the rootElement yet
370
            providers.getInstanceInjector = function () {
371
                return instanceInjector ? instanceInjector : instanceInjector = $rootElement.data('$injector') || angular.injector();
372
            };
373
374
            broadcast = function broadcast(eventName, params) {
375
                if (events) {
376
                    $rootScope.$broadcast(eventName, params);
377
                }
378
                if (debug) {
379
                    $log.info(eventName, params);
380
                }
381
            };
382
383
            function reject(e) {
384
                var deferred = $q.defer();
385
                $log.error(e.message);
386
                deferred.reject(e);
387
                return deferred.promise;
388
            }
389
390
            return {
391
                _broadcast: broadcast,
392
393
                _$log: $log,
394
395
                /**
396
                 * Returns the files cache used by the loaders to store the files currently loading
397
                 * @returns {*}
398
                 */
399
                _getFilesCache: function getFilesCache() {
400
                    return filesCache;
401
                },
402
403
                /**
404
                 * Let the service know that it should monitor angular.module because files are loading
405
                 * @param watch boolean
406
                 */
407
                toggleWatch: function toggleWatch(watch) {
408
                    if (watch) {
409
                        recordDeclarations.push(true);
410
                    } else {
411
                        recordDeclarations.pop();
412
                    }
413
                },
414
415
                /**
416
                 * Let you get a module config object
417
                 * @param moduleName String the name of the module
418
                 * @returns {*}
419
                 */
420
                getModuleConfig: function getModuleConfig(moduleName) {
421
                    if (!angular.isString(moduleName)) {
422
                        throw new Error('You need to give the name of the module to get');
423
                    }
424
                    if (!modules[moduleName]) {
425
                        return null;
426
                    }
427
                    return angular.copy(modules[moduleName]);
428
                },
429
430
                /**
431
                 * Let you define a module config object
432
                 * @param moduleConfig Object the module config object
433
                 * @returns {*}
434
                 */
435
                setModuleConfig: function setModuleConfig(moduleConfig) {
436
                    if (!angular.isObject(moduleConfig)) {
437
                        throw new Error('You need to give the module config object to set');
438
                    }
439
                    modules[moduleConfig.name] = moduleConfig;
440
                    return moduleConfig;
441
                },
442
443
                /**
444
                 * Returns the list of loaded modules
445
                 * @returns {string[]}
446
                 */
447
                getModules: function getModules() {
448
                    return regModules;
449
                },
450
451
                /**
452
                 * Let you check if a module has been loaded into Angular or not
453
                 * @param modulesNames String/Object a module name, or a list of module names
454
                 * @returns {boolean}
455
                 */
456
                isLoaded: function isLoaded(modulesNames) {
457
                    var moduleLoaded = function moduleLoaded(module) {
458
                        var isLoaded = regModules.indexOf(module) > -1;
459
                        if (!isLoaded) {
460
                            isLoaded = !!moduleExists(module);
461
                        }
462
                        return isLoaded;
463
                    };
464
                    if (angular.isString(modulesNames)) {
465
                        modulesNames = [modulesNames];
466
                    }
467
                    if (angular.isArray(modulesNames)) {
468
                        var i, len;
469
                        for (i = 0, len = modulesNames.length; i < len; i++) {
470
                            if (!moduleLoaded(modulesNames[i])) {
471
                                return false;
472
                            }
473
                        }
474
                        return true;
475
                    } else {
476
                        throw new Error('You need to define the module(s) name(s)');
477
                    }
478
                },
479
480
                /**
481
                 * Given a module, return its name
482
                 * @param module
483
                 * @returns {String}
484
                 */
485
                _getModuleName: getModuleName,
486
487
                /**
488
                 * Returns a module if it exists
489
                 * @param moduleName
490
                 * @returns {module}
491
                 */
492
                _getModule: function getModule(moduleName) {
493
                    try {
494
                        return ngModuleFct(moduleName);
495
                    } catch (e) {
496
                        // this error message really suxx
497
                        if (/No module/.test(e) || e.message.indexOf('$injector:nomod') > -1) {
498
                            e.message = 'The module "' + stringify(moduleName) + '" that you are trying to load does not exist. ' + e.message;
499
                        }
500
                        throw e;
501
                    }
502
                },
503
504
                /**
505
                 * Check if a module exists and returns it if it does
506
                 * @param moduleName
507
                 * @returns {boolean}
508
                 */
509
                moduleExists: moduleExists,
510
511
                /**
512
                 * Load the dependencies, and might try to load new files depending on the config
513
                 * @param moduleName (String or Array of Strings)
514
                 * @param localParams
515
                 * @returns {*}
516
                 * @private
517
                 */
518
                _loadDependencies: function _loadDependencies(moduleName, localParams) {
519
                    var loadedModule,
520
                        requires,
521
                        diff,
522
                        promisesList = [],
523
                        self = this;
524
525
                    moduleName = self._getModuleName(moduleName);
526
527
                    if (moduleName === null) {
528
                        return $q.when();
529
                    } else {
530
                        try {
531
                            loadedModule = self._getModule(moduleName);
532
                        } catch (e) {
533
                            return reject(e);
534
                        }
535
                        // get unloaded requires
536
                        requires = self.getRequires(loadedModule);
537
                    }
538
539
                    angular.forEach(requires, function (requireEntry) {
540
                        // If no configuration is provided, try and find one from a previous load.
541
                        // If there isn't one, bail and let the normal flow run
542
                        if (angular.isString(requireEntry)) {
543
                            var config = self.getModuleConfig(requireEntry);
544
                            if (config === null) {
545
                                moduleCache.push(requireEntry); // We don't know about this module, but something else might, so push it anyway.
546
                                return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
547
                            }
548
                            requireEntry = config;
549
                            // ignore the name because it's probably not a real module name
550
                            config.name = undefined;
551
                        }
552
553
                        // Check if this dependency has been loaded previously
554
                        if (self.moduleExists(requireEntry.name)) {
555
                            // compare against the already loaded module to see if the new definition adds any new files
556
                            diff = requireEntry.files.filter(function (n) {
557
                                return self.getModuleConfig(requireEntry.name).files.indexOf(n) < 0;
558
                            });
559
560
                            // If the module was redefined, advise via the console
561
                            if (diff.length !== 0) {
562
                                self._$log.warn('Module "', moduleName, '" attempted to redefine configuration for dependency. "', requireEntry.name, '"\n Additional Files Loaded:', diff);
563
                            }
564
565
                            // Push everything to the file loader, it will weed out the duplicates.
566
                            if (angular.isDefined(self.filesLoader)) {
567
                                // if a files loader is defined
568
                                promisesList.push(self.filesLoader(requireEntry, localParams).then(function () {
569
                                    return self._loadDependencies(requireEntry);
570
                                }));
571
                            } else {
572
                                return reject(new Error('Error: New dependencies need to be loaded from external files (' + requireEntry.files + '), but no loader has been defined.'));
573
                            }
574
                            return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
575
                        } else if (angular.isArray(requireEntry)) {
576
                            var files = [];
577
                            angular.forEach(requireEntry, function (entry) {
578
                                // let's check if the entry is a file name or a config name
579
                                var config = self.getModuleConfig(entry);
580
                                if (config === null) {
581
                                    files.push(entry);
582
                                } else if (config.files) {
583
                                    files = files.concat(config.files);
584
                                }
585
                            });
586
                            if (files.length > 0) {
587
                                requireEntry = {
588
                                    files: files
589
                                };
590
                            }
591
                        } else if (angular.isObject(requireEntry)) {
592
                            if (requireEntry.hasOwnProperty('name') && requireEntry['name']) {
593
                                // The dependency doesn't exist in the module cache and is a new configuration, so store and push it.
594
                                self.setModuleConfig(requireEntry);
595
                                moduleCache.push(requireEntry['name']);
596
                            }
597
                        }
598
599
                        // Check if the dependency has any files that need to be loaded. If there are, push a new promise to the promise list.
600
                        if (angular.isDefined(requireEntry.files) && requireEntry.files.length !== 0) {
601
                            if (angular.isDefined(self.filesLoader)) {
602
                                // if a files loader is defined
603
                                promisesList.push(self.filesLoader(requireEntry, localParams).then(function () {
604
                                    return self._loadDependencies(requireEntry);
605
                                }));
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
606
                            } else {
607
                                return reject(new Error('Error: the module "' + requireEntry.name + '" is defined in external files (' + requireEntry.files + '), but no loader has been defined.'));
608
                            }
609
                        }
610
                    });
611
612
                    // Create a wrapper promise to watch the promise list and resolve it once everything is done.
613
                    return $q.all(promisesList);
614
                },
615
616
                /**
617
                 * Inject new modules into Angular
618
                 * @param moduleName
619
                 * @param localParams
620
                 * @param real
621
                 */
622
                inject: function inject(moduleName) {
623
                    var localParams = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
624
                    var real = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
625
626
                    var self = this,
627
                        deferred = $q.defer();
628
                    if (angular.isDefined(moduleName) && moduleName !== null) {
629
                        if (angular.isArray(moduleName)) {
630
                            var promisesList = [];
631
                            angular.forEach(moduleName, function (module) {
632
                                promisesList.push(self.inject(module, localParams, real));
633
                            });
634
                            return $q.all(promisesList);
635
                        } else {
636
                            self._addToLoadList(self._getModuleName(moduleName), true, real);
637
                        }
638
                    }
639
                    if (modulesToLoad.length > 0) {
640
                        var res = modulesToLoad.slice(); // clean copy
641
                        var loadNext = function loadNext(moduleName) {
642
                            moduleCache.push(moduleName);
643
                            modulePromises[moduleName] = deferred.promise;
644
                            self._loadDependencies(moduleName, localParams).then(function success() {
645
                                try {
646
                                    justLoaded = [];
647
                                    _register(providers, moduleCache, localParams);
648
                                } catch (e) {
649
                                    self._$log.error(e.message);
650
                                    deferred.reject(e);
651
                                    return;
652
                                }
653
654
                                if (modulesToLoad.length > 0) {
655
                                    loadNext(modulesToLoad.shift()); // load the next in list
656
                                } else {
657
                                        deferred.resolve(res); // everything has been loaded, resolve
658
                                    }
659
                            }, function error(err) {
660
                                deferred.reject(err);
661
                            });
662
                        };
663
664
                        // load the first in list
665
                        loadNext(modulesToLoad.shift());
666
                    } else if (localParams && localParams.name && modulePromises[localParams.name]) {
667
                        return modulePromises[localParams.name];
668
                    } else {
669
                        deferred.resolve();
670
                    }
671
                    return deferred.promise;
672
                },
673
674
                /**
675
                 * Get the list of required modules/services/... for this module
676
                 * @param module
677
                 * @returns {Array}
678
                 */
679
                getRequires: function getRequires(module) {
680
                    var requires = [];
681
                    angular.forEach(module.requires, function (requireModule) {
682
                        if (regModules.indexOf(requireModule) === -1) {
683
                            requires.push(requireModule);
684
                        }
685
                    });
686
                    return requires;
687
                },
688
689
                /**
690
                 * Invoke the new modules & component by their providers
691
                 * @param providers
692
                 * @param queue
693
                 * @param moduleName
694
                 * @param reconfig
695
                 * @private
696
                 */
697
                _invokeQueue: _invokeQueue,
698
699
                /**
700
                 * Check if a module has been invoked and registers it if not
701
                 * @param args
702
                 * @param moduleName
703
                 * @returns {boolean} is new
704
                 */
705
                _registerInvokeList: _registerInvokeList,
706
707
                /**
708
                 * Register a new module and loads it, executing the run/config blocks if needed
709
                 * @param providers
710
                 * @param registerModules
711
                 * @param params
712
                 * @private
713
                 */
714
                _register: _register,
715
716
                /**
717
                 * Add a module name to the list of modules that will be loaded in the next inject
718
                 * @param name
719
                 * @param force
720
                 * @private
721
                 */
722
                _addToLoadList: _addToLoadList,
723
724
                /**
725
                 * Unregister modules (you shouldn't have to use this)
726
                 * @param modules
727
                 */
728
                _unregister: function _unregister(modules) {
729
                    if (angular.isDefined(modules)) {
730
                        if (angular.isArray(modules)) {
731
                            angular.forEach(modules, function (module) {
732
                                regInvokes[module] = undefined;
733
                            });
734
                        }
735
                    }
736
                }
737
            };
738
        }];
739
740
        // Let's get the list of loaded modules & components
741
        this._init(angular.element(window.document));
742
    }]);
743
744
    var bootstrapFct = angular.bootstrap;
745
    angular.bootstrap = function (element, modules, config) {
746
        // Clean state from previous bootstrap
747
        regModules = ['ng', 'oc.lazyLoad'];
748
        regInvokes = {};
749
        regConfigs = [];
750
        modulesToLoad = [];
751
        realModules = [];
752
        recordDeclarations = [];
753
        broadcast = angular.noop;
754
        runBlocks = {};
755
        justLoaded = [];
756
        // we use slice to make a clean copy
757
        angular.forEach(modules.slice(), function (module) {
758
            _addToLoadList(module, true, true);
759
        });
760
        return bootstrapFct(element, modules, config);
761
    };
762
763
    var _addToLoadList = function _addToLoadList(name, force, real) {
764
        if ((recordDeclarations.length > 0 || force) && angular.isString(name) && modulesToLoad.indexOf(name) === -1) {
765
            modulesToLoad.push(name);
766
            if (real) {
767
                realModules.push(name);
768
            }
769
        }
770
    };
771
772
    var ngModuleFct = angular.module;
773
    angular.module = function (name, requires, configFn) {
774
        _addToLoadList(name, false, true);
775
        return ngModuleFct(name, requires, configFn);
776
    };
777
778
    // CommonJS package manager support:
779
    if (typeof module !== 'undefined' && typeof exports !== 'undefined' && module.exports === exports) {
780
        module.exports = 'oc.lazyLoad';
781
    }
782
})(angular, window);
783
(function (angular) {
784
    'use strict';
785
786
    angular.module('oc.lazyLoad').directive('ocLazyLoad', ["$ocLazyLoad", "$compile", "$animate", "$parse", "$timeout", function ($ocLazyLoad, $compile, $animate, $parse, $timeout) {
787
        return {
788
            restrict: 'A',
789
            terminal: true,
790
            priority: 1000,
791
            compile: function compile(element, attrs) {
792
                // we store the content and remove it before compilation
793
                var content = element[0].innerHTML;
794
                element.html('');
795
796
                return function ($scope, $element, $attr) {
797
                    var model = $parse($attr.ocLazyLoad);
798
                    $scope.$watch(function () {
799
                        return model($scope) || $attr.ocLazyLoad; // it can be a module name (string), an object, an array, or a scope reference to any of this
800
                    }, function (moduleName) {
801
                        if (angular.isDefined(moduleName)) {
802
                            $ocLazyLoad.load(moduleName).then(function () {
803
                                // Attach element contents to DOM and then compile them.
804
                                // This prevents an issue where IE invalidates saved element objects (HTMLCollections)
805
                                // of the compiled contents when attaching to the parent DOM.
806
                                $animate.enter(content, $element);
807
                                // get the new content & compile it
808
                                $compile($element.contents())($scope);
809
                            });
810
                        }
811
                    }, true);
812
                };
813
            }
814
        };
815
    }]);
816
})(angular);
817
(function (angular) {
818
    'use strict';
819
820
    angular.module('oc.lazyLoad').config(["$provide", function ($provide) {
821
        $provide.decorator('$ocLazyLoad', ["$delegate", "$q", "$window", "$interval", function ($delegate, $q, $window, $interval) {
822
            var uaCssChecked = false,
823
                useCssLoadPatch = false,
824
                anchor = $window.document.getElementsByTagName('head')[0] || $window.document.getElementsByTagName('body')[0];
825
826
            /**
827
             * Load a js/css file
828
             * @param type
829
             * @param path
830
             * @param params
831
             * @returns promise
832
             */
833
            $delegate.buildElement = function buildElement(type, path, params) {
834
                var deferred = $q.defer(),
835
                    el,
836
                    loaded,
837
                    filesCache = $delegate._getFilesCache(),
838
                    cacheBuster = function cacheBuster(url) {
839
                    var dc = new Date().getTime();
840
                    if (url.indexOf('?') >= 0) {
841
                        if (url.substring(0, url.length - 1) === '&') {
842
                            return url + '_dc=' + dc;
843
                        }
844
                        return url + '&_dc=' + dc;
845
                    } else {
846
                        return url + '?_dc=' + dc;
847
                    }
848
                };
849
850
                // Store the promise early so the file load can be detected by other parallel lazy loads
851
                // (ie: multiple routes on one page) a 'true' value isn't sufficient
852
                // as it causes false positive load results.
853
                if (angular.isUndefined(filesCache.get(path))) {
854
                    filesCache.put(path, deferred.promise);
855
                }
856
857
                // Switch in case more content types are added later
858
                switch (type) {
859
                    case 'css':
860
                        el = $window.document.createElement('link');
861
                        el.type = 'text/css';
862
                        el.rel = 'stylesheet';
863
                        el.href = params.cache === false ? cacheBuster(path) : path;
864
                        break;
865
                    case 'js':
866
                        el = $window.document.createElement('script');
867
                        el.src = params.cache === false ? cacheBuster(path) : path;
868
                        break;
869
                    default:
870
                        filesCache.remove(path);
871
                        deferred.reject(new Error('Requested type "' + type + '" is not known. Could not inject "' + path + '"'));
872
                        break;
873
                }
874
                el.onload = el['onreadystatechange'] = function (e) {
0 ignored issues
show
Bug introduced by
The variable el seems to not be initialized for all possible execution paths.
Loading history...
875
                    if (el['readyState'] && !/^c|loade/.test(el['readyState']) || loaded) return;
0 ignored issues
show
Bug introduced by
The variable el seems to not be initialized for all possible execution paths.
Loading history...
876
                    el.onload = el['onreadystatechange'] = null;
877
                    loaded = 1;
878
                    $delegate._broadcast('ocLazyLoad.fileLoaded', path);
879
                    deferred.resolve(el);
880
                };
881
                el.onerror = function () {
882
                    filesCache.remove(path);
883
                    deferred.reject(new Error('Unable to load ' + path));
884
                };
885
                el.async = params.serie ? 0 : 1;
886
887
                var insertBeforeElem = anchor.lastChild;
888
                if (params.insertBefore) {
889
                    var element = angular.element(angular.isDefined(window.jQuery) ? params.insertBefore : document.querySelector(params.insertBefore));
890
                    if (element && element.length > 0) {
891
                        insertBeforeElem = element[0];
892
                    }
893
                }
894
                insertBeforeElem.parentNode.insertBefore(el, insertBeforeElem);
895
896
                /*
897
                 The event load or readystatechange doesn't fire in:
898
                 - PhantomJS 1.9 (headless webkit browser)
899
                 - iOS < 6       (default mobile browser)
900
                 - Android < 4.4 (default mobile browser)
901
                 - Safari < 6    (desktop browser)
902
                 */
903
                if (type == 'css') {
904
                    if (!uaCssChecked) {
905
                        var ua = $window.navigator.userAgent.toLowerCase();
906
907
                        if (ua.indexOf('phantomjs/1.9') > -1) {
908
                            // PhantomJS ~1.9
909
                            useCssLoadPatch = true;
910
                        } else if (/iP(hone|od|ad)/.test($window.navigator.platform)) {
911
                            // iOS < 6
912
                            var v = $window.navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/);
913
                            var iOSVersion = parseFloat([parseInt(v[1], 10), parseInt(v[2], 10), parseInt(v[3] || 0, 10)].join('.'));
914
                            useCssLoadPatch = iOSVersion < 6;
915
                        } else if (ua.indexOf('android') > -1) {
916
                            // Android < 4.4
917
                            var androidVersion = parseFloat(ua.slice(ua.indexOf('android') + 8));
918
                            useCssLoadPatch = androidVersion < 4.4;
919
                        } else if (ua.indexOf('safari') > -1) {
920
                            // Safari < 6
921
                            var versionMatch = ua.match(/version\/([\.\d]+)/i);
922
                            useCssLoadPatch = versionMatch && versionMatch[1] && parseFloat(versionMatch[1]) < 6;
923
                        }
924
                    }
925
926
                    if (useCssLoadPatch) {
927
                        var tries = 1000; // * 20 = 20000 miliseconds
928
                        var interval = $interval(function () {
929
                            try {
930
                                el.sheet.cssRules;
0 ignored issues
show
introduced by
The result of the property access to el.sheet.cssRules is not used.
Loading history...
Bug introduced by
The variable el seems to not be initialized for all possible execution paths.
Loading history...
931
                                $interval.cancel(interval);
932
                                el.onload();
933
                            } catch (e) {
934
                                if (--tries <= 0) {
935
                                    el.onerror();
936
                                }
937
                            }
938
                        }, 20);
939
                    }
940
                }
941
942
                return deferred.promise;
943
            };
944
945
            return $delegate;
946
        }]);
947
    }]);
948
})(angular);
949
(function (angular) {
950
    'use strict';
951
952
    angular.module('oc.lazyLoad').config(["$provide", function ($provide) {
953
        $provide.decorator('$ocLazyLoad', ["$delegate", "$q", function ($delegate, $q) {
954
            /**
955
             * The function that loads new files
956
             * @param config
957
             * @param params
958
             * @returns {*}
959
             */
960
            $delegate.filesLoader = function filesLoader(config) {
961
                var params = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
962
963
                var cssFiles = [],
964
                    templatesFiles = [],
965
                    jsFiles = [],
966
                    promises = [],
967
                    cachePromise = null,
0 ignored issues
show
Unused Code introduced by
The assignment to cachePromise seems to be never used. If you intend to free memory here, this is not necessary since the variable leaves the scope anyway.
Loading history...
968
                    filesCache = $delegate._getFilesCache();
969
970
                $delegate.toggleWatch(true); // start watching angular.module calls
971
972
                angular.extend(params, config);
973
974
                var pushFile = function pushFile(path) {
975
                    var file_type = null,
976
                        m;
977
                    if (angular.isObject(path)) {
978
                        file_type = path.type;
979
                        path = path.path;
980
                    }
981
                    cachePromise = filesCache.get(path);
982
                    if (angular.isUndefined(cachePromise) || params.cache === false) {
983
984
                        // always check for requirejs syntax just in case
985
                        if ((m = /^(css|less|html|htm|js)?(?=!)/.exec(path)) !== null) {
986
                            // Detect file type using preceding type declaration (ala requireJS)
987
                            file_type = m[1];
988
                            path = path.substr(m[1].length + 1, path.length); // Strip the type from the path
989
                        }
990
991
                        if (!file_type) {
992
                            if ((m = /[.](css|less|html|htm|js)?((\?|#).*)?$/.exec(path)) !== null) {
993
                                // Detect file type via file extension
994
                                file_type = m[1];
995
                            } else if (!$delegate.jsLoader.hasOwnProperty('ocLazyLoadLoader') && $delegate.jsLoader.hasOwnProperty('requirejs')) {
996
                                // requirejs
997
                                file_type = 'js';
998
                            } else {
999
                                $delegate._$log.error('File type could not be determined. ' + path);
1000
                                return;
1001
                            }
1002
                        }
1003
1004
                        if ((file_type === 'css' || file_type === 'less') && cssFiles.indexOf(path) === -1) {
1005
                            cssFiles.push(path);
1006
                        } else if ((file_type === 'html' || file_type === 'htm') && templatesFiles.indexOf(path) === -1) {
1007
                            templatesFiles.push(path);
1008
                        } else if (file_type === 'js' || jsFiles.indexOf(path) === -1) {
1009
                            jsFiles.push(path);
1010
                        } else {
1011
                            $delegate._$log.error('File type is not valid. ' + path);
1012
                        }
1013
                    } else if (cachePromise) {
1014
                        promises.push(cachePromise);
1015
                    }
1016
                };
1017
1018
                if (params.serie) {
1019
                    pushFile(params.files.shift());
1020
                } else {
1021
                    angular.forEach(params.files, function (path) {
1022
                        pushFile(path);
1023
                    });
1024
                }
1025
1026
                if (cssFiles.length > 0) {
1027
                    var cssDeferred = $q.defer();
1028
                    $delegate.cssLoader(cssFiles, function (err) {
1029
                        if (angular.isDefined(err) && $delegate.cssLoader.hasOwnProperty('ocLazyLoadLoader')) {
1030
                            $delegate._$log.error(err);
1031
                            cssDeferred.reject(err);
1032
                        } else {
1033
                            cssDeferred.resolve();
1034
                        }
1035
                    }, params);
1036
                    promises.push(cssDeferred.promise);
1037
                }
1038
1039
                if (templatesFiles.length > 0) {
1040
                    var templatesDeferred = $q.defer();
1041
                    $delegate.templatesLoader(templatesFiles, function (err) {
1042
                        if (angular.isDefined(err) && $delegate.templatesLoader.hasOwnProperty('ocLazyLoadLoader')) {
1043
                            $delegate._$log.error(err);
1044
                            templatesDeferred.reject(err);
1045
                        } else {
1046
                            templatesDeferred.resolve();
1047
                        }
1048
                    }, params);
1049
                    promises.push(templatesDeferred.promise);
1050
                }
1051
1052
                if (jsFiles.length > 0) {
1053
                    var jsDeferred = $q.defer();
1054
                    $delegate.jsLoader(jsFiles, function (err) {
1055
                        if (angular.isDefined(err) && ($delegate.jsLoader.hasOwnProperty("ocLazyLoadLoader") || $delegate.jsLoader.hasOwnProperty("requirejs"))) {
1056
                            $delegate._$log.error(err);
1057
                            jsDeferred.reject(err);
1058
                        } else {
1059
                            jsDeferred.resolve();
1060
                        }
1061
                    }, params);
1062
                    promises.push(jsDeferred.promise);
1063
                }
1064
1065
                if (promises.length === 0) {
1066
                    var deferred = $q.defer(),
1067
                        err = "Error: no file to load has been found, if you're trying to load an existing module you should use the 'inject' method instead of 'load'.";
1068
                    $delegate._$log.error(err);
1069
                    deferred.reject(err);
1070
                    return deferred.promise;
1071
                } else if (params.serie && params.files.length > 0) {
1072
                    return $q.all(promises).then(function () {
1073
                        return $delegate.filesLoader(config, params);
1074
                    });
1075
                } else {
1076
                    return $q.all(promises)['finally'](function (res) {
1077
                        $delegate.toggleWatch(false); // stop watching angular.module calls
1078
                        return res;
1079
                    });
1080
                }
1081
            };
1082
1083
            /**
1084
             * Load a module or a list of modules into Angular
1085
             * @param module Mixed the name of a predefined module config object, or a module config object, or an array of either
1086
             * @param params Object optional parameters
1087
             * @returns promise
1088
             */
1089
            $delegate.load = function (originalModule) {
1090
                var originalParams = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
1091
1092
                var self = this,
1093
                    config = null,
1094
                    deferredList = [],
1095
                    deferred = $q.defer(),
1096
                    errText;
1097
1098
                // clean copy
1099
                var module = angular.copy(originalModule);
1100
                var params = angular.copy(originalParams);
1101
1102
                // If module is an array, break it down
1103
                if (angular.isArray(module)) {
1104
                    // Resubmit each entry as a single module
1105
                    angular.forEach(module, function (m) {
1106
                        deferredList.push(self.load(m, params));
1107
                    });
1108
1109
                    // Resolve the promise once everything has loaded
1110
                    $q.all(deferredList).then(function (res) {
1111
                        deferred.resolve(res);
1112
                    }, function (err) {
1113
                        deferred.reject(err);
1114
                    });
1115
1116
                    return deferred.promise;
1117
                }
1118
1119
                // Get or Set a configuration depending on what was passed in
1120
                if (angular.isString(module)) {
1121
                    config = self.getModuleConfig(module);
1122
                    if (!config) {
1123
                        config = {
1124
                            files: [module]
1125
                        };
1126
                    }
1127
                } else if (angular.isObject(module)) {
1128
                    // case {type: 'js', path: lazyLoadUrl + 'testModule.fakejs'}
1129
                    if (angular.isDefined(module.path) && angular.isDefined(module.type)) {
1130
                        config = {
1131
                            files: [module]
1132
                        };
1133
                    } else {
1134
                        config = self.setModuleConfig(module);
1135
                    }
1136
                }
1137
1138
                if (config === null) {
1139
                    var moduleName = self._getModuleName(module);
1140
                    errText = 'Module "' + (moduleName || 'unknown') + '" is not configured, cannot load.';
1141
                    $delegate._$log.error(errText);
1142
                    deferred.reject(new Error(errText));
1143
                    return deferred.promise;
1144
                } else {
1145
                    // deprecated
1146
                    if (angular.isDefined(config.template)) {
1147
                        if (angular.isUndefined(config.files)) {
1148
                            config.files = [];
1149
                        }
1150
                        if (angular.isString(config.template)) {
1151
                            config.files.push(config.template);
1152
                        } else if (angular.isArray(config.template)) {
1153
                            config.files.concat(config.template);
1154
                        }
1155
                    }
1156
                }
1157
1158
                var localParams = angular.extend({}, params, config);
1159
1160
                // if someone used an external loader and called the load function with just the module name
1161
                if (angular.isUndefined(config.files) && angular.isDefined(config.name) && $delegate.moduleExists(config.name)) {
1162
                    return $delegate.inject(config.name, localParams, true);
1163
                }
1164
1165
                $delegate.filesLoader(config, localParams).then(function () {
1166
                    $delegate.inject(null, localParams).then(function (res) {
1167
                        deferred.resolve(res);
1168
                    }, function (err) {
1169
                        deferred.reject(err);
1170
                    });
1171
                }, function (err) {
1172
                    deferred.reject(err);
1173
                });
1174
1175
                return deferred.promise;
1176
            };
1177
1178
            // return the patched service
1179
            return $delegate;
1180
        }]);
1181
    }]);
1182
})(angular);
1183
(function (angular) {
1184
    'use strict';
1185
1186
    angular.module('oc.lazyLoad').config(["$provide", function ($provide) {
1187
        $provide.decorator('$ocLazyLoad', ["$delegate", "$q", function ($delegate, $q) {
1188
            /**
1189
             * cssLoader function
1190
             * @type Function
1191
             * @param paths array list of css files to load
1192
             * @param callback to call when everything is loaded. We use a callback and not a promise
1193
             * @param params object config parameters
1194
             * because the user can overwrite cssLoader and it will probably not use promises :(
1195
             */
1196
            $delegate.cssLoader = function (paths, callback, params) {
1197
                var promises = [];
1198
                angular.forEach(paths, function (path) {
1199
                    promises.push($delegate.buildElement('css', path, params));
1200
                });
1201
                $q.all(promises).then(function () {
1202
                    callback();
1203
                }, function (err) {
1204
                    callback(err);
1205
                });
1206
            };
1207
            $delegate.cssLoader.ocLazyLoadLoader = true;
1208
1209
            return $delegate;
1210
        }]);
1211
    }]);
1212
})(angular);
1213
(function (angular) {
1214
    'use strict';
1215
1216
    angular.module('oc.lazyLoad').config(["$provide", function ($provide) {
1217
        $provide.decorator('$ocLazyLoad', ["$delegate", "$q", function ($delegate, $q) {
1218
            /**
1219
             * jsLoader function
1220
             * @type Function
1221
             * @param paths array list of js files to load
1222
             * @param callback to call when everything is loaded. We use a callback and not a promise
1223
             * @param params object config parameters
1224
             * because the user can overwrite jsLoader and it will probably not use promises :(
1225
             */
1226
            $delegate.jsLoader = function (paths, callback, params) {
1227
                var promises = [];
1228
                angular.forEach(paths, function (path) {
1229
                    promises.push($delegate.buildElement('js', path, params));
1230
                });
1231
                $q.all(promises).then(function () {
1232
                    callback();
1233
                }, function (err) {
1234
                    callback(err);
1235
                });
1236
            };
1237
            $delegate.jsLoader.ocLazyLoadLoader = true;
1238
1239
            return $delegate;
1240
        }]);
1241
    }]);
1242
})(angular);
1243
(function (angular) {
1244
    'use strict';
1245
1246
    angular.module('oc.lazyLoad').config(["$provide", function ($provide) {
1247
        $provide.decorator('$ocLazyLoad', ["$delegate", "$templateCache", "$q", "$http", function ($delegate, $templateCache, $q, $http) {
1248
            /**
1249
             * templatesLoader function
1250
             * @type Function
1251
             * @param paths array list of css files to load
1252
             * @param callback to call when everything is loaded. We use a callback and not a promise
1253
             * @param params object config parameters for $http
1254
             * because the user can overwrite templatesLoader and it will probably not use promises :(
1255
             */
1256
            $delegate.templatesLoader = function (paths, callback, params) {
1257
                var promises = [],
1258
                    filesCache = $delegate._getFilesCache();
1259
1260
                angular.forEach(paths, function (url) {
1261
                    var deferred = $q.defer();
1262
                    promises.push(deferred.promise);
1263
                    $http.get(url, params).then(function (response) {
1264
                        var data = response.data;
1265
                        if (angular.isString(data) && data.length > 0) {
1266
                            angular.forEach(angular.element(data), function (node) {
1267
                                if (node.nodeName === 'SCRIPT' && node.type === 'text/ng-template') {
1268
                                    $templateCache.put(node.id, node.innerHTML);
1269
                                }
1270
                            });
1271
                        }
1272
                        if (angular.isUndefined(filesCache.get(url))) {
1273
                            filesCache.put(url, true);
1274
                        }
1275
                        deferred.resolve();
1276
                    })['catch'](function (response) {
1277
                        deferred.reject(new Error('Unable to load template file "' + url + '": ' + response.data));
1278
                    });
1279
                });
1280
                return $q.all(promises).then(function () {
1281
                    callback();
1282
                }, function (err) {
1283
                    callback(err);
1284
                });
1285
            };
1286
            $delegate.templatesLoader.ocLazyLoadLoader = true;
1287
1288
            return $delegate;
1289
        }]);
1290
    }]);
1291
})(angular);
1292
// Array.indexOf polyfill for IE8
1293
if (!Array.prototype.indexOf) {
1294
        Array.prototype.indexOf = function (searchElement, fromIndex) {
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type Array. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
1295
                var k;
1296
1297
                // 1. Let O be the result of calling ToObject passing
1298
                //    the this value as the argument.
1299
                if (this == null) {
1300
                        throw new TypeError('"this" is null or not defined');
1301
                }
1302
1303
                var O = Object(this);
1304
1305
                // 2. Let lenValue be the result of calling the Get
1306
                //    internal method of O with the argument "length".
1307
                // 3. Let len be ToUint32(lenValue).
1308
                var len = O.length >>> 0;
1309
1310
                // 4. If len is 0, return -1.
1311
                if (len === 0) {
1312
                        return -1;
1313
                }
1314
1315
                // 5. If argument fromIndex was passed let n be
1316
                //    ToInteger(fromIndex); else let n be 0.
1317
                var n = +fromIndex || 0;
1318
1319
                if (Math.abs(n) === Infinity) {
1320
                        n = 0;
1321
                }
1322
1323
                // 6. If n >= len, return -1.
1324
                if (n >= len) {
1325
                        return -1;
1326
                }
1327
1328
                // 7. If n >= 0, then Let k be n.
1329
                // 8. Else, n<0, Let k be len - abs(n).
1330
                //    If k is less than 0, then let k be 0.
1331
                k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
1332
1333
                // 9. Repeat, while k < len
1334
                while (k < len) {
1335
                        // a. Let Pk be ToString(k).
1336
                        //   This is implicit for LHS operands of the in operator
1337
                        // b. Let kPresent be the result of calling the
1338
                        //    HasProperty internal method of O with argument Pk.
1339
                        //   This step can be combined with c
1340
                        // c. If kPresent is true, then
1341
                        //    i.  Let elementK be the result of calling the Get
1342
                        //        internal method of O with the argument ToString(k).
1343
                        //   ii.  Let same be the result of applying the
1344
                        //        Strict Equality Comparison Algorithm to
1345
                        //        searchElement and elementK.
1346
                        //  iii.  If same is true, return k.
1347
                        if (k in O && O[k] === searchElement) {
1348
                                return k;
1349
                        }
1350
                        k++;
1351
                }
1352
                return -1;
1353
        };
1354
}